package com.hoffm59.facebookcontactsync.fragment;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.List;

import org.json.JSONException;
import org.json.JSONObject;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;

import com.facebook.Request;
import com.facebook.Response;
import com.facebook.Session;
import com.facebook.SessionState;
import com.facebook.UiLifecycleHelper;
import com.facebook.model.GraphObject;
import com.facebook.model.GraphUser;
import com.facebook.widget.ProfilePictureView;
import com.hoffm59.facebookcontactsync.PickerActivity;
import com.hoffm59.facebookcontactsync.PickerApplication;
import com.hoffm59.facebookcontactsync.R;
import com.hoffm59.facebookcontactsync.util.BaseListElement;
import com.hoffm59.facebookcontactsync.util.ContactHelper;

/**
 * @author HOFFM59
 */
public class SelectionFragment
        extends Fragment
        implements OnClickListener {

    private static final String TAG = "SelectionFragment";

    private static final int REAUTH_ACTIVITY_CODE = 100;

    private ProfilePictureView profilePictureView;
    private TextView userNameView;
    private ListView listView;
    private Button syncBtn;
    private List<BaseListElement> listElements;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

        super.onCreateView(inflater, container, savedInstanceState);

        this.uiHelper = new UiLifecycleHelper(this.getActivity(), this.callback);
        this.uiHelper.onCreate(savedInstanceState);

        final View view = inflater.inflate(R.layout.fragment_selection, container, false);

        // Find the user's profile picture custom view
        this.profilePictureView = (ProfilePictureView) view.findViewById(R.id.fragment_selection_profile_pic);
        this.profilePictureView.setCropped(true);

        // Find the user's name view
        this.userNameView = (TextView) view.findViewById(R.id.fragment_selection_user_name);

        this.syncBtn = (Button) view.findViewById(R.id.fragment_selection_sync_contatcs);
        this.syncBtn.setOnClickListener(this);

        // Find the list view
        this.listView = (ListView) view.findViewById(R.id.fragment_selection_list);

        // Set up the list view items, based on a list of
        // BaseListElement items
        this.listElements = new ArrayList<BaseListElement>();
        // Add an item for the friend picker
        this.listElements.add(new PeopleListElement(0));

        if (savedInstanceState != null) {
            // Restore the state for each list element
            for (final BaseListElement listElement : this.listElements) {
                listElement.restoreState(savedInstanceState);
            }
        }

        // Set the list view adapter
        this.listView.setAdapter(new ActionListAdapter(this.getActivity(), R.id.fragment_selection_list,
                this.listElements));

        // Check for an open session
        final Session session = Session.getActiveSession();
        if ((session != null) && session.isOpened()) {
            // Get the user's data
            this.makeMeRequest(session);
        }

        return view;
    }

    @Override
    public void onClick(View v) {

        final BaseListElement userElement = this.listElements.get(0);
        if (userElement instanceof PeopleListElement) {

            final List<GraphUser> selectedUsers = ((PeopleListElement) userElement).getUsers();

            ContactHelper.syncContacts(this.getActivity(), selectedUsers);
        }
    }

    private void makeMeRequest(final Session session) {

        // Make an API call to get user data and define a
        // new callback to handle the response.
        final Request request = Request.newMeRequest(session, new Request.GraphUserCallback() {
            @Override
            public void onCompleted(GraphUser user, Response response) {

                // If the response is successful
                if (session == Session.getActiveSession()) {
                    if (user != null) {
                        // Set the id for the ProfilePictureView
                        // view that in turn displays the profile picture.
                        SelectionFragment.this.profilePictureView.setProfileId(user.getId());
                        // Set the Textview's text to the user's name.
                        SelectionFragment.this.userNameView.setText(user.getName());
                    }
                }
                if (response.getError() != null) {
                    // Handle errors, will do so later.
                }
            }
        });
        request.executeAsync();
    }

    private void onSessionStateChange(final Session session, SessionState state, Exception exception) {

        if ((session != null) && session.isOpened()) {
            // Get the user's data.
            this.makeMeRequest(session);
        }
    }

    private UiLifecycleHelper uiHelper;
    private final Session.StatusCallback callback = new Session.StatusCallback() {
        @Override
        public void call(final Session session, final SessionState state, final Exception exception) {

            SelectionFragment.this.onSessionStateChange(session, state, exception);
        }
    };

    private void startPickerActivity(Uri data, int requestCode) {

        final Intent intent = new Intent();
        intent.setData(data);
        intent.setClass(this.getActivity(), PickerActivity.class);
        this.startActivityForResult(intent, requestCode);
    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {

        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == SelectionFragment.REAUTH_ACTIVITY_CODE) {
            this.uiHelper.onActivityResult(requestCode, resultCode, data);
        } else if ((resultCode == Activity.RESULT_OK) && (requestCode >= 0) && (requestCode < this.listElements.size())) {
            this.listElements.get(requestCode).onActivityResult(data);
        }
    }

    @Override
    public void onResume() {

        super.onResume();
        this.uiHelper.onResume();
    }

    @Override
    public void onSaveInstanceState(Bundle bundle) {

        super.onSaveInstanceState(bundle);
        for (final BaseListElement listElement : this.listElements) {
            listElement.onSaveInstanceState(bundle);
        }
        this.uiHelper.onSaveInstanceState(bundle);
    }

    @Override
    public void onPause() {

        super.onPause();
        this.uiHelper.onPause();
    }

    @Override
    public void onDestroy() {

        super.onDestroy();
        this.uiHelper.onDestroy();
    }

    private class ActionListAdapter
            extends ArrayAdapter<BaseListElement> {
        private final List<BaseListElement> listElements;

        public ActionListAdapter(Context context, int resourceId, List<BaseListElement> listElements) {

            super(context, resourceId, listElements);
            this.listElements = listElements;
            // Set up as an observer for list item changes to
            // refresh the view.
            for (int i = 0; i < listElements.size(); i++) {
                listElements.get(i).setAdapter(this);
            }
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {

            View view = convertView;
            if (view == null) {
                final LayoutInflater inflater =
                        (LayoutInflater) SelectionFragment.this.getActivity().getSystemService(
                                Context.LAYOUT_INFLATER_SERVICE);
                view = inflater.inflate(R.layout.listitem, null);
            }

            final BaseListElement listElement = this.listElements.get(position);
            if (listElement != null) {
                view.setOnClickListener(listElement.getOnClickListener());
                final ImageView icon = (ImageView) view.findViewById(R.id.icon);
                final TextView text1 = (TextView) view.findViewById(R.id.text1);
                final TextView text2 = (TextView) view.findViewById(R.id.text2);
                if (icon != null) {
                    icon.setImageDrawable(listElement.getIcon());
                }
                if (text1 != null) {
                    text1.setText(listElement.getText1());
                }
                if (text2 != null) {
                    text2.setText(listElement.getText2());
                }
            }
            return view;
        }
    }

    private class PeopleListElement
            extends BaseListElement {

        private List<GraphUser> selectedUsers;
        private static final String FRIENDS_KEY = "friends";

        public PeopleListElement(int requestCode) {

            super(SelectionFragment.this.getActivity().getResources().getDrawable(R.drawable.action_people),
                    SelectionFragment.this.getActivity().getResources().getString(R.string.action_people),
                    SelectionFragment.this.getActivity().getResources().getString(R.string.action_people_default),
                    requestCode);

            this.selectedUsers = new ArrayList<GraphUser>();
        }

        @Override
        public View.OnClickListener getOnClickListener() {

            return new View.OnClickListener() {
                @Override
                public void onClick(View view) {

                    SelectionFragment.this.startPickerActivity(PickerActivity.FRIEND_PICKER,
                            PeopleListElement.this.getRequestCode());
                }
            };
        }

        @Override
        public void onActivityResult(Intent data) {

            this.selectedUsers =
                    ((PickerApplication) SelectionFragment.this.getActivity().getApplication()).getSelectedUsers();
            this.setUsersText();
            this.notifyDataChanged();
        }

        @Override
        public void onSaveInstanceState(Bundle bundle) {

            if (this.selectedUsers != null) {
                bundle.putByteArray(PeopleListElement.FRIENDS_KEY, this.getByteArray(this.selectedUsers));
            }
        }

        @Override
        public boolean restoreState(Bundle savedState) {

            final byte[] bytes = savedState.getByteArray(PeopleListElement.FRIENDS_KEY);
            if (bytes != null) {
                this.selectedUsers = this.restoreByteArray(bytes);
                this.setUsersText();
                return true;
            }
            return false;
        }

        public List<GraphUser> getUsers() {

            return this.selectedUsers;
        }

        private void setUsersText() {

            String text = null;
            if (this.selectedUsers != null) {
                // If there is one friend
                if (this.selectedUsers.size() == 1) {
                    text =
                            String.format(SelectionFragment.this.getResources()
                                    .getString(R.string.single_user_selected), this.selectedUsers.get(0).getName());
                } else if (this.selectedUsers.size() == 2) {
                    // If there are two friends
                    text =
                            String.format(SelectionFragment.this.getResources().getString(R.string.two_users_selected),
                                    this.selectedUsers.get(0).getName(), this.selectedUsers.get(1).getName());
                } else if (this.selectedUsers.size() > 2) {
                    // If there are more than two friends
                    text =
                            String.format(
                                    SelectionFragment.this.getResources().getString(R.string.multiple_users_selected),
                                    this.selectedUsers.get(0).getName(), (this.selectedUsers.size() - 1));
                }
            }
            if (text == null) {
                // If no text, use the placeholder text
                text = SelectionFragment.this.getResources().getString(R.string.action_people_default);
            }
            // Set the text in list element. This will notify the
            // adapter that the data has changed to
            // refresh the list view.
            this.setText2(text);
        }

        private byte[] getByteArray(List<GraphUser> users) {

            // convert the list of GraphUsers to a list of String
            // where each element is the JSON representation of the
            // GraphUser so it can be stored in a Bundle
            final List<String> usersAsString = new ArrayList<String>(users.size());

            for (final GraphUser user : users) {
                usersAsString.add(user.getInnerJSONObject().toString());
            }
            try {
                final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
                new ObjectOutputStream(outputStream).writeObject(usersAsString);
                return outputStream.toByteArray();
            } catch (final IOException e) {
                Log.e(SelectionFragment.TAG, "Unable to serialize users.", e);
            }
            return null;
        }

        private List<GraphUser> restoreByteArray(byte[] bytes) {

            try {
                @SuppressWarnings("unchecked")
                final List<String> usersAsString =
                        (List<String>) (new ObjectInputStream(new ByteArrayInputStream(bytes))).readObject();
                if (usersAsString != null) {
                    final List<GraphUser> users = new ArrayList<GraphUser>(usersAsString.size());
                    for (final String user : usersAsString) {
                        final GraphUser graphUser = GraphObject.Factory.create(new JSONObject(user), GraphUser.class);
                        users.add(graphUser);
                    }
                    return users;
                }
            } catch (final ClassNotFoundException e) {
                Log.e(SelectionFragment.TAG, "Unable to deserialize users.", e);
            } catch (final IOException e) {
                Log.e(SelectionFragment.TAG, "Unable to deserialize users.", e);
            } catch (final JSONException e) {
                Log.e(SelectionFragment.TAG, "Unable to deserialize users.", e);
            }
            return null;
        }
    }
}